package cc.rengu.igas.bsps.core.service.base;

import cc.rengu.igas.bsps.common.constant.BspsTreeNodeConstant;
import cc.rengu.igas.bsps.common.enums.NoCheckTxnInfoEnum;
import cc.rengu.igas.bsps.common.enums.RespCodeEnum;
import cc.rengu.igas.bsps.core.model.UserSessionInfo;
import cc.rengu.igas.bsps.core.realize.UserPermissionCheckService;
import cc.rengu.igas.bsps.core.realize.impl.UserPermissionCheckServiceImpl;
import cc.rengu.igas.share.core.realize.impl.RiskMonitorServiceImpl;
import cc.rengu.oltp.service.base.BizBaseService;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.model.BizResponse;
import cc.rengu.oltp.service.model.RiskMonitorInfo;
import cc.rengu.oltp.service.realize.RiskMonitorService;
import cc.rengu.oltp.utility.annotation.BIZ_AFTER;
import cc.rengu.oltp.utility.annotation.BIZ_BEFORE;
import cc.rengu.oltp.utility.annotation.BIZ_PROCESS;
import cc.rengu.oltp.utility.util.*;
import com.alibaba.fastjson.JSON;
import org.apache.commons.codec.binary.Base64;

/**
 * @author zhangxuran
 * @version 1.0
 * @date 2020.06.16
 */
public abstract class BankManagerService extends BizBaseService {
    @Override
    protected Object beforeBizProcess() throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        /* Step1：查找BIZ_BEFORE的注解*/
        invokeBizAnnoMethod(BIZ_BEFORE.class);

        String txnNum = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TXN_NUM);
        String instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.INST_ID);
        String session = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.SESSION);
        String userId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.USER_ID);
        String managerId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.MANAGER_ID);
        String channelId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.CHANNEL_ID);
        /* Step2：检查验签状态 */
        String verifySignFlag = xmlTreeUtil.getXmlTreeStringValue(BspsTreeNodeConstant.VERIFY_SIGN_FLAG);
        if (AppParamConstant.NO.equals(verifySignFlag)) {
            rglog.error("签名验证失败，交易码:<{}>", txnNum);
            if (StringUtil.isEmptyOrNull(session)) {
                rglog.error("session 为null");
                throw new BizException(RespCodeEnum.USER_NOT_LOGIN_OR_SESSION_IS_EXPIRED.getRespCode(), RespCodeEnum.USER_NOT_LOGIN_OR_SESSION_IS_EXPIRED.getRespDesc());
            }

            String sessionObjStr = RedisUtil.onceGet(session);
            if (sessionObjStr == null) {
                rglog.error("根据会话标识<{}>获取会话缓存失败", session);
                throw new BizException(RespCodeEnum.USER_NOT_LOGIN_OR_SESSION_IS_EXPIRED.getRespCode(), RespCodeEnum.USER_NOT_LOGIN_OR_SESSION_IS_EXPIRED.getRespDesc());
            }

            UserSessionInfo userSessionInfo = JSON.parseObject(sessionObjStr, UserSessionInfo.class);
            if (null == userSessionInfo) {
                rglog.error("根据会话标识<{}>获取会话缓存成功，转为UserSessionInfo失败。", session);
                throw new BizException(RespCodeEnum.USER_NOT_LOGIN_OR_SESSION_IS_EXPIRED.getRespCode(), RespCodeEnum.USER_NOT_LOGIN_OR_SESSION_IS_EXPIRED.getRespDesc());
            }

            throw new BizException(RespCodeEnum.VERIFY_SIGN_ERROR.getRespCode(), RespCodeEnum.VERIFY_SIGN_ERROR.getRespDesc());
        }

        /* Step3:session检查 */
        if (!StringUtil.isEmptyOrNull(session)) {
            UserPermissionCheckService userService = new UserPermissionCheckServiceImpl();
            UserSessionInfo userSessionInfo = userService.checkSessionStatus(instId, session);
            if (!NoCheckTxnInfoEnum.checkTxnNo(txnNum)) {
                //检查用户登录状态
                userSessionInfo = userService.checkUserLoginSessionStatus(instId, session, userId);
                //检查该用户角色是否有该交易权限
                if (!userService.checkManagerUserPermission(instId, userId, txnNum, channelId)) {
                    rglog.error("用户权限校验失败:{}", userId + ":" + txnNum);
                    throw new BizException(RespCodeEnum.USER_NO_AUTHORITY.getRespCode(), RespCodeEnum.USER_NO_AUTHORITY.getRespDesc());
                }
            }
            String phone = xmlTreeUtil.getXmlTreeStringValue("phoneNo");
            if (!StringUtil.isEmptyOrNull(phone)) {
                phone = descriptData(userSessionInfo.getSensitiveKey(), phone);
                if (phone == null) {
                    rglog.error("解密数据失败，拒绝交易");
                } else {
                    xmlTreeUtil.setXmlTreeStringValue("phone", phone);
                }
            }
        }

        /* Step5：交易处理前客户化 */
        return bizBeforeCust();
    }

    @Override
    protected BizResponse callBizService(Object request) throws Exception {
        /* Step1：公共业务处理 */

        /* Step2：查找BIZ_PROCESS的注解 */
        invokeBizAnnoMethod(BIZ_PROCESS.class);

        /* Step3：业务逻辑处理*/
        return bizProcessCust(request);
    }

    @Override
    protected void afterBizProcess(BizResponse response) throws Exception {
        /* Step1：业务调用后公共处理*/

        /* Step2：查找BIZ_AFTER的注解 */
        invokeBizAnnoMethod(BIZ_AFTER.class);

        /* Step3：业务调用后客户化处理 */
        bizAfterCust(response);
    }

    @Override
    protected RiskMonitorInfo getRiskMonitorInfo() {
        RiskMonitorInfo riskMonitorInfo = new RiskMonitorInfo();
        RiskMonitorService riskMonitorService = new RiskMonitorServiceImpl();
        riskMonitorInfo.setTransMonitor(riskMonitorService);
        return riskMonitorInfo;
    }

    /**
     * 业务处理前客户化处理
     *
     * @return 业务处理实体类
     */
    protected abstract Object bizBeforeCust() throws Exception;

    /**
     * 业务逻辑处理客户化处理
     *
     * @param request 业务处理前的返回实体类
     * @return 业务处理结果
     */
    protected abstract BizResponse bizProcessCust(Object request) throws Exception;

    /**
     * 业务处理后逻辑处理
     *
     * @param response 业务调用响应信息
     */
    protected abstract void bizAfterCust(Object response) throws Exception;

    private String descriptData(String sensitiveKey, String encData) throws Exception {
        //TODO 目前只支持AES PKCS7-CBC
        String algorithmType = "AES";
        if ("AES".equals(algorithmType)) {
            String descData = AESUtil.pkcs7CbcDecryptData(Base64.decodeBase64(sensitiveKey), Base64.decodeBase64(encData));
            return descData;
        } else {
            byte[] bEncData = Base64.decodeBase64(encData);
            byte[] descData = new byte[bEncData.length];
            int retCode = SM4Util.sms4(bEncData, bEncData.length, ByteUtil.hexStringToByte(sensitiveKey), descData, 0);
            if (retCode == 0) {
                for (int i = 0; i < descData.length; i++) {
                    if (descData[i] == 0) {
                        return new String(descData, 0, i);
                    }
                }
                return new String(descData);
            }
        }

        return null;
    }
}
